﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using PS.DataTypes;
using System.Xml.Serialization;
using PS.Engine.Helper;

namespace PS.Engine.GameComponents
{
    [Serializable]
    [XmlRoot("Polygon")]
    [XmlInclude(typeof(Vector2))]
    internal class PolygonComponent : BaseGameComponent
    {
        #region Private Members

        private StandardBasicEffect effect = null;
        private List<Line> edges = null;
        private VertexPositionColor[] triangulatedVertices = null;

        private bool triangulated = false;

        #endregion Private Members

        #region Constructors

        public PolygonComponent(Game game)
            : base(game)
        {
            Vertices = new List<Vector2>();
            color = Color.Gray;
        }

        #endregion

        #region Properties
        
        [XmlArray("Vertices")]
        [XmlArrayItem("Vertex")]
        public List<Vector2> Vertices { get; set; }

        public List<Line> Edges
        {
            get
            {
                if (edges == null)
                {
                    LoadEdges();
                }

                return edges;
            }
        }

        private StandardBasicEffect Effect
        {
            get
            {
                if (effect == null)
                {
                    effect = new StandardBasicEffect(GraphicsDevice);
                }

                return effect;
            }
        }

        #endregion Properties

        #region Private Methods

        private void LoadEdges()
        {
            edges = new List<Line>();

            for (int i = 0; i < Vertices.Count; i++)
            {
                // get first vertex
                Vector2 vertex1 = Vertices[i];

                // get second vertex
                // close the circle: get next or first index
                int vertex2Index = (i+1) % Vertices.Count;
                Vector2 vertex2 = Vertices[vertex2Index];

                // add edge
                Line newEdge = new Line(vertex1, vertex2);
                edges.Add(newEdge);
            }
        }

        /// <summary>
        /// Triangulates the set of Vertices so it will be drawn correcrly
        /// </summary>
        public void Triangulate()
        {
            // calculate center point
            Vector3 centerPoint = CalculateCenterPoint();

            // set up triangles
            triangulatedVertices = new VertexPositionColor[Vertices.Count * 3];
            int triangulatedVertexIndex = 0;
            foreach (Line edge in Edges)
            {
                triangulatedVertices[triangulatedVertexIndex] = new VertexPositionColor(centerPoint, color);
                triangulatedVertices[triangulatedVertexIndex + 1] = new VertexPositionColor(new Vector3(edge.From, 0), color);
                triangulatedVertices[triangulatedVertexIndex + 2] = new VertexPositionColor(new Vector3(edge.To, 0), color);

                triangulatedVertexIndex += 3;
            }

            triangulated = true;
        }

        /// <summary>
        /// Calculate the center point needed for triangulation.
        /// The polygon will be irregular, so this isn't the actual center of the polygon
        /// but it will do for now, as we only need an extra point to make the triangles with.</summary>
        private Vector3 CalculateCenterPoint()
        {
            float sumX = 0;
            float sumY = 0;

            foreach (Vector2 vertex in Vertices)
            {
                sumX += vertex.X;
                sumY += vertex.Y;
            }

            return new Vector3(sumX / Vertices.Count, sumY / Vertices.Count, 0);
        }
        
        /// <summary>
        /// Draw the polygon. If you haven't called Triangulate yet, I wil do it for you.
        /// </summary>
        public override void Draw(GameTime gameTime)
        {
            if (!triangulated)
            {
                Triangulate();
            }

            // Initialize an array of indices of type short.
            short[] triangleStripIndices = new short[triangulatedVertices.Length];

            // Populate the array with references to indices in the vertex buffer.
            for (int i = 0; i < triangulatedVertices.Length; i++)
            {
                triangleStripIndices[i] = (short)i;
            }

            Effect.CurrentTechnique.Passes[0].Apply();
            GraphicsDevice.DrawUserIndexedPrimitives<VertexPositionColor>(
                PrimitiveType.TriangleStrip,
                triangulatedVertices,
                0,  // vertex buffer offset to add to each element of the index buffer
                triangulatedVertices.Length,  // number of vertices to draw
                triangleStripIndices,
                0,  // first index element to read
                triangulatedVertices.Length - 2  // number of primitives to draw
            );
        }

        #endregion Private Methods
    }
}
